#include "frame_util.h"

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis/hiredis.h>
#include "data_type.h"

extern uint8_t *sn;

/**
 * @brief 查找数据包里面的结束字符位置
 * 
 * @param str 原始数据缓冲区
 * @param c 结束字符
 * @param len 原始数据长度
 * @return uint8_t* 找到字符后返回其后部分（包括结尾的空字符）
 */
uint8_t* find_substring(uint8_t *str, uint8_t c, uint16_t len) {
    int i = 0;
    if (str == NULL) {
        return NULL;
    }
    // 遍历字符串直到找到目标字符或到达结尾
    printf("FindSub:");
    while (i < len) {
        printf("%02X ", *(str + i));
        if (i > 0 && (*(str + i -1)<<8 | *(str+i)) == 0xA5A3) {
            i++;
        } else if(i > 0 && i < (len-1) && ((*(str+i-1)<<8 | *(str+i)) == 0xA3A3 || (*(str + i)<<8 | *(str+i+1)) == 0xA3A3)){
            i = i+2;
        } else if(*(str + i) == c) {
            break;
        } else {
            i++;
        }
    }
    printf("\n");
    // 找到字符后返回其后部分（包括结尾的空字符）
    return (*(str+i) == c) ? str + i : NULL;
}

/**
 * @brief 编码数据帧：添加帧头、转义字符、CRC 校验和帧尾
 * 
 * @param input 原始数据缓冲区
 * @param input_len 原始数据长度
 * @param output 输出缓冲区指针（由函数分配内存）
 * @return uint16_t 编码后数据长度
 */
uint16_t encode_frame(uint8_t *input, uint16_t input_len, uint8_t **output) {
    // 计算编码后的最大可能长度
    uint16_t max_encoded_len = input_len * 2 + 3; // A5 + data + A3 + CRC (2 bytes)
    *output = (uint8_t *)malloc(max_encoded_len);
    if(*output == NULL) {
        perror("Memory allocation failed");
        exit(EXIT_FAILURE);
    }

    uint16_t j = 0;
    // 添加帧头
    (*output)[j++] = 0xA5;

    // 编码有效数据
    for(uint16_t i = 0; i < input_len; i++) {
        if(input[i] == 0xA5) {
            (*output)[j++] = 0xA5;
            (*output)[j++] = 0xA3;
        }
        else if(input[i] == 0xA3) {
            (*output)[j++] = 0xA3;
            (*output)[j++] = 0xA3;
        }
        else {
            (*output)[j++] = input[i];
        }
    }

    // 计算 CRC（不包括帧头和 CRC 自身）
    uint16_t crc = rtu_crc((*output) + 1, j - 1);

    // 插入 CRC（低位在前）
    
    
    (*output)[j++] = crc & 0xFF;
    (*output)[j++] = (crc >> 8) & 0xFF;
    // 添加帧尾
    (*output)[j++] = 0xA3;

    return j;
}

/**
 * @brief 解码数据帧：去除帧头、转义字符、验证 CRC 和帧尾
 * 
 * @param input 接收到的数据缓冲区
 * @param length 接收到的数据长度
 * @param output 输出缓冲区指针（由函数分配内存）
 * @return uint16_t 解码后数据长度，若失败则返回 0
 */
uint16_t decode_frame(uint8_t *input, uint16_t length, uint8_t **output) {
    if(length < 4) { // 最小帧长度：A5 + 数据 + CRC + A3
        return 0;
    }

    // 查找最后一个 A3 作为帧尾
    uint16_t frame_end = length - 1;
    while(frame_end > 0 && input[frame_end] != 0xA3)
        frame_end--;

    if(frame_end == 0) // 没有找到帧尾
        return 0;

    uint16_t payload_crc_len = frame_end; // 包括 CRC
    uint16_t payload_start = 1; // 帧头后

    // 分配输出缓冲区
    *output = (uint8_t *)malloc(payload_start + (payload_crc_len - 3)); // 不包括 CRC 和 A3
    if(*output == NULL) {
        perror("Memory allocation failed");
        exit(EXIT_FAILURE);
    }

    uint16_t j = 0;
    for(uint16_t i = payload_start; i < payload_crc_len; i++) {
        if(i + 1 < payload_crc_len) {
            if(input[i] == 0xA5 && input[i + 1] == 0xA3) {
                (*output)[j++] = 0xA5;
                i++; // 跳过下一个字节
            }
            else if(input[i] == 0xA3 && input[i + 1] == 0xA3) {
                (*output)[j++] = 0xA3;
                i++; // 跳过下一个字节
            }
            else {
                (*output)[j++] = input[i];
            }
        }
        else {
            // 最后一个字节无法形成配对，按原样处理或忽略
            (*output)[j++] = input[i];
        }
    }

    // 提取 CRC（高位在后）
    uint16_t received_crc = (input[payload_crc_len - 1] << 8) | input[payload_crc_len - 2];

    // 计算解码后数据的 CRC
    uint16_t calculated_crc = rtu_crc(&input[1], length-4);
    printf("CRC:%04X\n", calculated_crc);
    if(received_crc != calculated_crc) {
        printf("CRC 校验失败。\n");
        free(*output);
        *output = NULL;
        return 0;
    }

    return j;
}

/**
 * @brief 解析数据帧，把解决装到结构体数据里面
 * 
 * @param input 需要解析的数据帧
 * @param length 解析的数据长度
 * @return LoraData 解析后的结构体数据
 */
void parse_frame(uint8_t *input, uint16_t length, LoraData *frame){
    if (length < 5){
        return NULL;
    }
    frame->dev_num = input[0]<<8 | input[1];
    frame->type = input[2];
    frame->len = input[3]<<8 | input[4];
    memmove(frame->data, input+5, length -5);
}
